#include <QApplication>
#include <QDebug>
#include <QSettings>
#include <QStandardItemModel>
#include <QTimer>
#include <QUrl>

#include "Server.h"
#include "ServerChecker.h"
#include "ServerManager.h"

#ifdef Q_OS_WIN32
#include "WindowsPing.h"
#endif

ServerManager::ServerManager( ServerChecker *serverChecker, QObject *parent ) :
    QObject(parent),
    URL_MAX_LENGTH( 50 ),
    mModel( new QStandardItemModel(this) ),
    mServerChecker( serverChecker )
{
    mServerChecker->setServers( &mServers );
}

ServerManager::~ServerManager()
{
    saveServers();
}

int ServerManager::createServer( const QString &name, const QUrl &url )
{
    int id = mServers.size();

    Server *server = new Server( id, this );
    server->setName( name );
    server->setUrl( url );
    mServers.append( server );

    setServer( id );
    setHeaders();

    connect( server, SIGNAL(dataChanged(int)),
             this, SLOT(setServer(int)) );

    return id;
}

void ServerManager::deleteServer( int id )
{
    Server *server = mServers.takeAt( id );
    server->deleteLater();
    mModel->removeRow( id );
}

void ServerManager::editServer( int id, ServerManager::ServerSettings settings )
{
    if ( id < 0 || mServers.size() <= id )
    {
        return;
    }

    Server *server = mServers.at( id );
    server->setCheckStats( settings.checkStats );
    server->setMaxLoadOne( settings.maxLoad1 );
    server->setMaxLoadFive( settings.maxLoad5 );
    server->setMaxLoadFifteen( settings.maxLoad15 );
    server->setMaxPing( settings.maxPing );
    server->setUrl( settings.url );

    setServer( id );
    setHeaders();
}

void ServerManager::loadServers()
{
    // Load.
    QSettings settings(
        QSettings::IniFormat,
        QSettings::UserScope,
        qApp->organizationName(),
        qApp->applicationName()
    );

    int size = settings.beginReadArray( "servers" );
    for ( int id = 0; id < size; ++id )
    {
        settings.setArrayIndex( id );

        QString name = settings.value("name").toString();
        QUrl url = settings.value("url").toUrl();
        int maxPing = settings.value("maxPing").toInt();
        bool checkStats = settings.value("checkStats").toBool();
        float maxLoadOne = settings.value("maxLoad1").toFloat();
        float maxLoadFive = settings.value("maxLoad5").toFloat();
        float maxLoadFifteen = settings.value("maxLoad15").toFloat();

        int id = createServer( name, url );
        Server *server = mServers.at( id );
        server->setMaxPing( maxPing );
        server->setCheckStats( checkStats );
        server->setMaxLoadOne( maxLoadOne );
        server->setMaxLoadFive( maxLoadFive );
        server->setMaxLoadFifteen( maxLoadFifteen );

        connect( server, SIGNAL(dataChanged(int)),
                 this, SLOT(setServer(int)) );
    }
    settings.endArray();

    setHeaders();
}

QAbstractItemModel* ServerManager::model() const
{
    Q_ASSERT( mModel != NULL );

    return mModel;
}

void ServerManager::saveServers() const
{
    QSettings settings(
        QSettings::IniFormat,
        QSettings::UserScope,
        qApp->organizationName(),
        qApp->applicationName()
    );

    settings.beginWriteArray( "servers" );
    int size = mServers.size();
    for ( int id = 0; id < size; ++id )
    {
        Server *server = mServers.at( id );
        settings.setArrayIndex( id );

        settings.setValue( "name", server->name() );
        settings.setValue( "url", server->url().toString() );
        settings.setValue( "maxPing", server->maxPing() );
        settings.setValue( "checkStats", server->checkStats() );
        settings.setValue( "maxLoad1", server->maxLoadOne() );
        settings.setValue( "maxLoad5", server->maxLoadFive() );
        settings.setValue( "maxLoad15", server->maxLoadFifteen() );
    }
    settings.endArray();

    settings.sync();
}

const ServerList* ServerManager::servers() const
{
    return &mServers;
}

void ServerManager::setColumn( int row, Column column, bool ok, const QString &text )
{
    QStandardItem *item = new QStandardItem( text );
    item->setTextAlignment( Qt::AlignRight | Qt::AlignVCenter );
    if ( ok )
    {
        item->setForeground( Qt::black );
    }
    else
    {
        item->setForeground( Qt::red );
    }
    mModel->setItem( row, column, item );
}

void ServerManager::setHeaders()
{
    // Set header labels.
    mModel->setHeaderData( ColName, Qt::Horizontal, tr("Name") );
    mModel->setHeaderData( ColPing, Qt::Horizontal, tr("Ping") );
    mModel->setHeaderData( ColLoad1, Qt::Horizontal, tr("Load (1 min)") );
    mModel->setHeaderData( ColLoad5, Qt::Horizontal, tr("Load (5 min)") );
    mModel->setHeaderData( ColLoad15, Qt::Horizontal, tr("Load (15 min)") );
}

void ServerManager::setServer( int id )
{
    // Check id.
    if ( id >= mServers.size() )
    {
        return;
    }

    bool alarm = false;
    bool ok;
    QString text;

    Server *server = mServers.at( id );
    QStandardItem *item = NULL;

    // Name.
    item = new QStandardItem( server->name() );
    mModel->setItem( id, ColName, item );

    // Ping.
    text = QString( "%1ms" ).arg( server->ping() );
    if ( server->ping() == Server::HOST_UNAVAILABLE )
    {
        text = "Host unavailable";
    }
    ok = server->ping() != Server::HOST_UNAVAILABLE &&
         server->ping() < server->maxPing();
    if ( !ok )
    {
        qDebug() << "Alarm: Ping";
        alarm = true;
    }
    setColumn( id, ColPing, ok, text );

    // Load 1.
    text = QString( "%1" ).arg( server->loadOne() );
    ok = server->loadOne() < server->maxLoadOne();
    if ( !ok )
    {
        qDebug() << "Alarm: Load 1";
        alarm = true;
    }
    setColumn( id, ColLoad1, ok, text );

    // Load 5.
    text = QString( "%1" ).arg( server->loadFive() );
    ok = server->loadFive() < server->maxLoadFive();
    if ( !ok )
    {
        qDebug() << "Alarm: Load 5";
        alarm = true;
    }
    setColumn( id, ColLoad5, ok, text );

    // Load 15.
    text = QString( "%1" ).arg( server->loadFifteen() );
    ok = server->loadFifteen() < server->maxLoadFifteen();
    if ( !ok )
    {
        qDebug() << "Alarm: Load 15";
        alarm = true;
    }
    setColumn( id, ColLoad15, ok, text );

    // Send alarm.
    if ( alarm )
    {
        emit serverCritical( id );
    }
}

void ServerManager::start()
{
    mServerChecker->start();
}
